﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.Resources
{
    #region Using Statements

    using System;
    using System.IO;
    using System.Net;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using KLF.Lib.Browser.DOM;
    using KLF.Lib.Browser.DOM.HTML;
    using System.Collections.Generic;
    using KLF.Lib.Browser.DOM.Interface;

    #endregion

    public delegate void AssignResource(ILoadableContent receiver, string source);

    /// <summary>
    /// Class that assists in loading external resources for a document 
    /// load request.
    /// </summary>
    public class ResourceLoader
    {
        #region Constructors

        public ResourceLoader(RequestContext context)
        {
            BaseUri = context.Uri;
            Context = context; 
            Requests = new Queue<ResourceRequest>();
            Pending = new List<ResourceRequest>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// The request context for the resource.
        /// </summary>
        internal RequestContext Context { get; set; }

        /// <summary>
        /// The base URI of the document in which the resource lives.
        /// </summary>
        internal Uri BaseUri { get; set; }

        /// <summary>
        /// The collection of requests that are out for fullfillment.
        /// </summary>
        internal List<ResourceRequest> Pending { get; set; }

        /// <summary>
        /// The entire collection of requests 
        /// </summary>
        internal Queue<ResourceRequest> Requests { get; set; }

        #endregion

        #region Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public bool AddResourceRequest(ResourceRequest request)
        {
            bool result = false;

            try
            {
                request.ResolvedUri = new Uri(BaseUri, new Uri(request.Path, UriKind.RelativeOrAbsolute));
                request.Context = new RequestContext(Context, Context.Window, request.ResolvedUri);
                Requests.Enqueue(request);
                result = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine("ResourceLoader: Failed to add request: {0}", ex.Message);
            }
            return result;
        }

        /// <summary>
        /// Runs through all of the pending requests.
        /// </summary>
        public void Execute()
        {
            do
            {
                int threadCount, completionThreads;

                ThreadPool.GetAvailableThreads(out threadCount, out completionThreads);

                if (threadCount > 0)
                {
                    for (int i = 0; i < Math.Min(threadCount, Requests.Count); i++)
                    {
                        try
                        {
                            ResourceRequest request = Requests.Dequeue();
                            ThreadStart ts = new ThreadStart(request.LoadRequest);

                            Thread worker = new Thread(ts);

                            lock (Pending)
                            {
                                Pending.Add(request);
                                request.ResouceLoaded += new EventHandler<ResourceRequestComplete>(request_ResouceLoaded);
                            }
                            worker.Start();
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("ResourceLoader: {0}", ex.Message);
                        }
                    }
                }

                if (Requests.Count > 0)
                {
                    Thread.Sleep(50);
                }
            }
            while (Requests.Count > 0);

            int wc;
            do
            {
                lock (Pending)
                {
                    wc = Pending.Count;
                }
                if (wc > 0)
                {
                    Console.WriteLine("Waiting for {0} threads to finish.", wc);
                    Thread.Sleep(250);
                }
            }
            while (wc > 0);
        }

        void request_ResouceLoaded(object sender, ResourceRequestComplete e)
        {
            lock (Pending)
            {
                Pending.Remove(e.Request);
            }
        }

        #endregion
    }
}
